原文在A gentle Introduction to React’s Higher Order Components
副标题是:如何在在高阶组件中使用条件性渲染
Higher order components缩写为 HOCs.可以用于各种用例, 这里集中在条件性渲染上.
不断增长的组件
假设有给TodoList
组件,实例代码如下:
1 | function App(props) { |
实际编程中,光有这些是远远不够的, 需要有为空, 长度为0,渲染中的状态
1 | function TodoList({ todos, isLoadingTodos }) { |
由此代码就显得复杂了.高阶组件可以让这个问题简化一下.
导入高阶组件
HOCS通常接收一个组件和可选的参数,然后返回一个输入组件(input component)的增强版本(enhanced components).
定义第一个高阶组件 withTodosNull
高阶组件返回 stateless-component或者 ES6 class 组件都可以,如果需要访问组件生命周期中的方法,或者是this.state
,应该要返回一个 ES6 class 组件
1 | function withTodosNull(Component) { |
这是一个三元操作符
,高阶组件根据 props 来决定是渲染null 还是组件本身.所有的 props 都向下传递.
改为 ES6的箭头函数,更容易理解:
1 | const withTodosNull = (Component) => (props) => |
最终的高阶函数完成了:
1 | const withTodosNull = (Component) => (props) => |
与 null 组件类似
1 | const withTodosEmpty = (Component) => (props) => |
如果在 input 子类的组件中使用 loading 时, 并不需要出传递其他的 props,可以用 ES6 的展开操作符 把 props 分割一下:
1 | const withLoadingIndicator = (Component) => ({ isLoadingTodos, ...others }) => |
最终在TodoList
组件中的使用:
1 | const withTodosNull = (Component) => (props) => |
高阶组件中的顺序也很重要,因为,前一个组件的条件满足,就直接返回了.
使用 Recompose 进一步改进代码
1 | import { compose } from 'recompose'; |
这个增强的过程就是:
1 | const TodoListWithConditionalRendering = withConditionalRenderings(TodoList); |
1 | import { compose } from 'recompose'; |
重用抽象的高阶组件
上面的组件适应于特定的渲染, 不能用在其他地方. 考虑到长期的使用,应该抽象出来,以便于其他的组价也可以使用
在withTodoNull
中添加一个 optional 负载,这个负载是一个函数,负责返回 true 或者 false, 用于决定最终的渲染结果:
1 | const withTodosNull = (Component, conditionalRenderingFn) => (props) => |
现在这个函数的名字就有点误导了, 改为:
1 | const withCondition = (Component, conditionalRenderingFn) => (props) => |
现在就可以用这个抽象的条件判断组件来实现具体的逻辑
1 | const withCondition = (Component, conditionalRenderingFn) => (props) => |
为了利于实现柯理化, 把负载的函数也分开传递:
1 | const withCondition = (conditionalRenderingFn) => (Component) => (props) => |
在使用时传递条件函数就可以了:
1 | import { compose } from 'recompose'; |
maybe 和 either 高阶组件
什么也不返回,或者返回输入组件的类型,在函数式编程中有名字的叫 Maybe.
1 | const withMaybe = (conditionalRenderingFn) => (Component) => (props) => |
两个组件返回其一的在函数式编程中称为 either
1 | const withEither = (conditionalRenderingFn, EitherComponent) => (Component) => (props) => |
最最终的版本
终于是最后一个版本了
1 | import { compose } from 'recompose'; |
确实赏心悦目的代码改进, 每一步都体现了程序员的思考过程.